Always declare your variables in JavaScript!

One mistake many people who are new to JavaScript often make is forgetting to declare their variables before using them. It is a mistake I often see when browsing through questions on StackOverflow. Instead of continuing to write very similar comments pointing out the problems with not declaring variables and how to avoid making this mistake, I decided to write this post so I can link to it in a more terse comment and give a more in depth explanation of the problem than I could in a SO comment.

Why undeclared variables are a problem

If you don't declare a variable in JavaScript before you use it, it becomes an implicit global. Implicit globals are a bad thing, they often lead to hard to debug problems. One common example of implicit globals causing problems is when a loop counter is global. Consider the following:

function foo () {
  for(i = 0; i < 4; i += 1) {
    console.log('foo ' + i);
  }
}

function bar () {
  for (i = 0; i < 3; i += 1) {
    foo();
    console.log('bar ' + i);
  }
}

bar();

You might expect this code to output:

foo 0
foo 1
foo 2
foo 3
bar 0
foo 0
foo 1
foo 2
foo 3
bar 1
foo 0
foo 1
foo 2
foo 3
bar 2

But it actually outputs:

foo 0
foo 1
foo 2
foo 3
bar 4

What's going on here? Because i was not declared before it was used, in either function, inside both functions i refers to the same variable in the global scope.

  1. When bar runs, i is set to 0 at the beginning of the loop.
  2. It then calls foo which sets i to 0 again and then iterates up to 4, satisfying it's loop condition, exiting the loop and then the foo function.
  3. When it returns to bar, that global variable i is still 4, so it prints bar 4.
  4. Then the loop in bar evaluates its condition, i < 3, because i is 4, the loop ends and bar returns after only one time around the loop.

This might be a hard to notice error in a simple example like this, in a more complex program this kind of error can cause hours of frustration. Even more so when the bug effects asynchronous code like XHR requests and timers.

Fixing the problem

You can avoid these kinds of errors by making sure you always declare your variables with either var, let, or const before using them:

function foo () {
  // Variables declared with var are function scoped, so it is a good practice
  // to declare them at the top of the function so their scope is clear when
  // reading the code.
  var i;
  for(i = 0; i < 4; i += 1) {
    console.log('foo ' + i);
  }
}

function bar () {
  // Since let is block scoped you should declare the variable at lowest scope
  // level possible. In this case, the initialization statement of the loop will
  // allow us to access it in the loop (including the condition statement and
  // final-expression statement).
  for (let i = 0; i < 3; i += 1) {
    foo();
    console.log('bar ' + i);
  }
  // i is not available here since it was declared in the for block
}

bar();

Spotting undeclared variables

Being human, even when we know we should declare our variables, all of us sometimes forget. The good news is there are a couple of things you can do to spot these kinds of errors.

Strict mode

ES5 added strict mode to JavaScript. Opting into strict mode gives you a number of benefits, one of which is alerting you to undeclared variables. Strict mode makes undeclared variables an error, so you will be sure to notice them.

Using a linter

Using a linter can help a lot with spotting undeclared variables as well as a plethora of other potential problems. There are a number of linters available for JavaScript these days.

  • jsLint, the original written by Douglas Crockford. It is very strict and opinionated. It "will hurt your feelings".
  • jsHint, less opinionated, but still helps find bugs and other potential problems.
  • esLint, highly customizable, has a set of recommended rules, but lets you decide which rules you want to opt into.
  • JavaScript Lint, I've not used this one and can't vouch for it's quality or lack there of.

There is probably a plug-in to use most of these for your favorite build tool, be it Gulp, Grunt or something else. Since I usually use Gulp, I make use of gulp-eslint on most of my projects.

Conclusion

As innocuous as not declaring your variables might seem at first, I hope this article helped you see it really is a serious issue that can cause all sorts of problems if not avoided. If you are new to JavaScript, you might benefit from reading another article I wrote recently about spotting bad JavaScript tutorials. There is a lot of bad information about JavaScript out there, learning how to spot it can protect you from picking up bad habits.

Previous: Spotting bad JavaScript tutorials